#!/usr/bin/python3
# -*- coding:utf-8 -*-
"""
@File: SeeService.py
@Author: lijk34925 
@Date: 2025/4/21 19:50
@desc: 请补充本模块功能说明
"""
import concurrent
import json
import re
import time
from concurrent.futures.thread import ThreadPoolExecutor
from copy import deepcopy
from datetime import datetime

import requests
from jsonpath import jsonpath

from AmlScripts.amlConfig import amlConfig
from BladeFerm.ApiModule.httpRequest import HttpRequest
from BladeFerm.Lib.LocalLogger import logger_local as logger
from BladeFerm.databases.DealSql.EnvConfig import EnvConfig


class SeeService:
    def __init__(self, config, sigAddr="10.20.182.242:8085", timeout=180):
        env_name = config.get("env", "unKnow")
        self.envConfig = EnvConfig(env_name, config=config)
        self.url = self.envConfig.seeConfig.get('url')                  # see平台地址
        self.user = self.envConfig.seeConfig.get('user')                # see平台登录用户
        self.pwd = self.envConfig.seeConfig.get('password')             # see平台APIKey
        self.zone = self.envConfig.seeConfig.get('zone')                # see平台的空间名称
        self.sigAddr = sigAddr          # 获取参数签名的javaServer服务地址
        self.environmentId = "001"      # 环境ID
        self.apps = list()              # 存储Application的应用信息
        self.components = list()        # 存储Component的应用信息
        self.appStatus = list()         # 存储Application的应用状态，由服务控制台获取
        self.isLocalSee = False         # 是否为自建see
        self.timeout = timeout          # 检查服务重启或部署后服务状态的超时时间
        self.client = HttpRequest()
        self.__judge_see_type()

    def __judge_see_type(self):
        """
        判断当前See服务类型，本地See还是远程See
        :return:
        """
        pattern = r'http.*//(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)/acm'
        res_flag = re.search(pattern, self.url, re.I)
        if res_flag:
            self.isLocalSee = True
            self.url = f"http://{res_flag.group(1)}:{res_flag.group(2)}/acm/api/v1/application"
            self.__get_zone_list()
            self.get_application_list()
            self.__get_component_list()
            logger.info(f"SeeServiceOperate初始化成功，url：{self.url}，zone：{self.zone}，envId: {self.environmentId}")
        else:
            self.isLocalSee = False
            logger.info(f"使用的是效能SEE平台！")

    def get_see_type(self):
        return self.isLocalSee

    def __getSignatureOfParams(self, method, params: dict) -> dict:
        """
        获取请求参数的签名
        :param method: 请求方法，GET/POST
        :param params: 请求参数
        :return:
        """
        params = {
          "usr": self.user,
          "pwd": self.pwd,
          "method": method,
          "action": params.get("Action"),
          "data": json.dumps(params)
        }
        headers = {"Content-Type": "application/json"}
        signature_address = f"http://{self.sigAddr}/fast-generator-data/see/params"
        result = requests.post(signature_address, json=params, headers=headers)
        if result.status_code == 200:
            signature_text = result.json().get('data')
            signature_json = json.loads(signature_text)
            return signature_json
        else:
            return {}

    def __common_request(self, method, params: dict):
        """
        通用请求方法
        :param method:
        :param params:
        :return:
        """
        signature_params = self.__getSignatureOfParams(method, params)
        if method == 'GET':
            response = self.client.doGetMethod(self.url, params=signature_params)
        else:
            response = self.client.doPost(self.url, data=signature_params)
        if response.status_code == 200:
            res_json = response.json()
            if isinstance(res_json, dict) and res_json.get("code", "") == "signature.error":
                logger.error(f"验证签名失败！")
                raise Exception(f"验证签名失败！")
            else:
                return response.json()
        else:
            raise Exception(f"请求失败！")

    def __get_zone_list(self):
        """
        获取环境列表，获取当前环境的ID
        :return:
        """
        params = {"Action": "EnvironmentList"}
        result = self.__common_request('GET', params)
        if result:
            for zone in result:
                if zone.get("envName") == self.zone:
                    self.environmentId = zone.get("envId")
        logger.info(f"当前{self.zone}环境ID：{self.environmentId}")

    def __filter_app_id(self, app_name):
        for unit in self.apps:
            if re.search(app_name, unit.get("name"), re.I):
                return unit.get("id")
        return None

    def __filter_component_id(self, app_name):
        for unit in self.components:
            if re.match(app_name, unit.get("name"), re.I):
                return unit.get("instanceId")
        return None

    def get_application_list(self, app_name=None):
        """
        获取应用列表
        {
          "Action": "ApplicationList", // 必填
          "SystemId": "应用系统ID", // 可选，应用id
          "Name": "应用系统名称, 模糊匹配", // 可选，应用名称
          "LifeCycle": "系统状态", // 可选，待部署:60 部署中:80 部署成功:100 部署失败:140
          "ProductId": "", // 可选，发布物id
          "ProductType": "",  // 可选，全量包: SEE.PRODUCT 补丁包:  SEE.PATCH
          "ProductVersion": "", // 可选，发布物版本
          "Category": "",  // 可选，发布物一级分类，对应发布物包的systemType|primaryType
          "ProductTypeName": "", // 可选，发布物二级分类，对应发布物包的appType|secondaryType
          "EnvironmentId": "", //可选，环境id 默认是001
          "ServiceType": "", //可选，普通应用：10，隐藏的应用：100，若不填默认查询普通应用
          "IsIncludeMonitorInfo": "" //可选，"true"/"false"，是否返回监视器信息
        }
        [{
          "id":"应用系统ID",
          "name":"应用系统名",
          "owner":"所有者",
          "contact":"联系方式",
          "description":"应用系统说明",
          "lifeCycle":"应用系统状态",
          "submitter":"创建者",
          "sysType":"应用系统类型",
          "lastModified":"最后修改时间",
          "groupId":"分组ID",
          "groupName":"分组名称",
          "productId":"发布物ID",
          "productName":"发布物名称",
          "productType":"发布物类型",
          "category":"发布物主类型",
          "productTypeName":"发布物次类型",
          "productVersion":"发布物版本",
          "poolId":"DOCKER集群ID",
          "productModel":"发布包生成类型",
          "autoUpgrade":"是否自动更新",
          "productStackName":"发布物栈名称",
          "productStackId":"发布物栈ID",
          "monitorInfo":"监视器信息"
        }]
        :return:
        """
        params = {"Action": "ApplicationList", "EnvironmentId": self.environmentId}
        if app_name:
            params.update({"SystemId": self.__filter_app_id(app_name)})
        result = self.__common_request('GET', params)
        if result:
            if not app_name:
                self.apps = deepcopy(result)

    def deploy_app(self, app_name):
        app_id = self.__filter_app_id(app_name)
        params = {
            "Action": "ApplicationDeployById",
            "SystemId": app_id,
            "EnvironmentId": self.environmentId
        }
        result = self.__common_request('POST', params)
        if result.get('Status') == 'fail':
            logger.error(f"应用部署失败，失败原因：{result.get('message')}")
            return False
        else:
            serialNo_tmp = result.get('SerialNo')
            return self.__get_app_deploy_status(app_id, serialNo_tmp)

    def __get_app_deploy_status(self, instanceId, serialNo):
        params = {
            "Action": "ApplicationStatus",       # 必填
            "InstanceId": instanceId,      # 必填 应用ID，可以通过ApplicationList接口获得指定的应用ID信息
            "SerialNo": serialNo,             # 必填 部署或者升级返回的日志序列号
            "EnvironmentId": self.environmentId  # 可选，环境id 默认是001
        }
        index = 0
        while index < self.timeout:
            result = self.__common_request('GET', params)
            status = result.get('Status')[0].get('Status')
            if status == 'success':
                return True
            elif status == 'fail':
                return False
            else:
                time.sleep(3)
                index += 3
        return False

    def app_operate(self, app_name, operate='restart'):
        options = {'restart': 'ApplicationRestart', 'stop': 'ApplicationStop',
                   'start': 'ApplicationStart', 'delete': 'ApplicationDelete'}
        app_id = self.__filter_app_id(app_name)
        params = {"Action": options.get(operate), "SystemId": app_id}
        result = self.__common_request('GET', params)
        if result.get('Status') == 'fail':
            logger.error(f"应用重启失败，失败原因：{result.get('message')}")
            return False
        else:
            serialNo_tmp = result.get('SerialNo')
            return self.__get_app_deploy_status(app_id, serialNo_tmp)

    def __get_component_list(self, component_id=None):
        """
        获取组件列表
        :param component_id:
        :return: [  {
            "serviceType": "RabbitMQ",
            "instanceId": "#aefeef5bcf724060a93b03b278cebf91",
            "serviceStatus": "READY",
            "serialNo": "2025041315505656656215_RabbitMQ",
            "environment": "001006",
          }]
        """
        params = {"Action": "ComponentList"}
        if component_id:
            params.update({"ComponentId": component_id})
        result = self.__common_request('POST', params)
        if component_id:
            return result[0] if result else None
        else:
            for unit in result:
                if unit.get('environment') == self.environmentId:
                    self.components.append(unit)
            # logger.info(f"组件列表：{self.components}")
            return self.components

    def component_operate(self, component_name, operate='restart'):
        """
        组件操作
        :param component_name:
        :param operate:
        :return:   {
            "SerialNo": "序列号",
            "ComponentId": "组件ID"
        }
        """
        options = {'restart': 'ComponentRestart', 'stop': 'ComponentStop',
                   'start': 'ComponentStart', 'delete': 'ComponentDelete'}
        params = {"Action": options.get(operate), "ComponentId": self.__filter_component_id(component_name)}
        result = self.__common_request('GET', params)
        component_id_tmp = result.get('ComponentId')
        index = 0
        while index < self.timeout:
            component_detail = self.__get_component_list(component_id_tmp)
            if component_detail.get('serviceStatus') == 'READY':
                return True
            time.sleep(1)
            index += 1
        return False

    def view_app_list(self):
        """
        获取服务控制台中应用状态列表
        :return:
        """
        params = {"Action": "MicrosvcNodeList", "EnvironmentId": self.environmentId}
        self.appStatus = self.__common_request('GET', params)
        return self.appStatus

    def __multi_thread_run(self, name_list: list, flag=False) -> list:
        """
        多线程并行执行
        :param server_list:
        :param env_name:
        :return:
        """
        last_result = list()
        with ThreadPoolExecutor(max_workers=len(name_list)) as executor:
            tasks = [executor.submit(self.__server_operate, server, flag) for server in name_list]
            for future in concurrent.futures.as_completed(tasks):
                try:
                    last_result.append(future.result())
                except Exception as exc:
                    logger.error(f"任务执行出错: {exc}")
        return last_result

    def __server_operate(self, server, checkStatus=False):
        """
        单个服务启动
        :param server: 服务名称
        :param checkStatus: 是否检查服务状态，然后区分操作类型
        :return:
        """
        server_status = None
        if checkStatus:
            server_status = self.__get_server_status(server)
        logger.info(f"======环境：{self.zone}: {server} 开始重启！======")
        if (not checkStatus) and server in ['ZooKeeper', 'RabbitMQ', 'Redis']:
            result = self.component_operate(server)
            comp_result_dict = {'Service': server, 'Result': result, 'Operation': 'restart'}
        elif checkStatus and server in ['ZooKeeper', 'RabbitMQ', 'Redis']:
            result = True
            logger.warning(f"======环境：{self.zone}:组件{server}状态未知， 请人工操作！======")
            comp_result_dict = {'Service': server, 'Result': True, 'Operation': 'restart'}
        elif (not checkStatus) and re.match(r"aml", server, re.I):
            result = self.deploy_app(server)
            comp_result_dict = {'Service': server, 'Result': result, 'Operation': 'deploy'}
        else:
            if checkStatus and server_status:
                logger.warning(f"======环境：{self.zone}:组件{server}已经是启动状态！======")
                result = True
            else:
                result = self.app_operate(server)
            comp_result_dict = {'Service': server, 'Result': result, 'Operation': 'restart'}
        if result:
            logger.info(f"======环境：{self.zone}: {server} 重启成功！======")
        else:
            logger.warning(f"======环境：{self.zone}: {server} 重启失败！======")
        return comp_result_dict

    def restart_servers(self, servers) -> dict:
        """
        重启服务
        :param servers:
        :return:
        """
        operation_result = dict()
        operation_result.update({'URL': self.url, 'Zone': self.zone})
        operation_list = list()
        for server_name in servers:
            if not isinstance(server_name, list):
                server_name = [server_name]
            operation_list.extend(self.__multi_thread_run(server_name))
        operation_result.update({'Operations': operation_list})
        return operation_result

    def __get_server_status(self, name):
        for unit in self.appStatus:
            if re.search(name, unit.get('service_name')):
                return unit.get('status') == '100'
        return None

    def check_server(self, servers):
        """
        检查服务状态
        :param servers:
        :return:
        """
        operation_result = dict()
        operation_result.update({'URL': self.url, 'Zone': self.zone})
        last_result = list()
        SeeServices.view_app_list()
        for server in servers:
            if not isinstance(server, list):
                server = [server]
            result = self.__multi_thread_run(server, True)
            last_result.append(result)
        operation_result.update({'Operations': last_result})
        return last_result

    @staticmethod
    def judge_restart_result(res_dict: dict) -> bool:
        all_results = jsonpath(res_dict, '$..Result')
        return all(all_results) if all_results else False


if __name__ == '__main__':
    env = 'mysql_auto'
    server_list = ['pbs-server', 'amlps-server', 'amlcom-server', 'amlcom-ui2']
    amlConfig.update({'env': env})
    SeeServices = SeeService(amlConfig, sigAddr="127.0.0.1:8085")
    # res_dict = SeeServices.restart_servers(server_list)
    # res = SeeServices.judge_restart_result(res_dict)
    res = SeeServices.view_app_list()